/*
 * 文件名：ArithmeticUtils.java
 * 版权：Copyright 2018-2019 Love. Co. Ltd. All Rights Reserved.
 * 描述：Anthony.CHONG
 * 修改人：Anthony.CHONG
 * 修改时间：2020年04月24日
 * 系统名称：xiaole
 */
package com.sinbader.xiaole.common.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * <一句话功能简述>
 * <功能详细描述>
 *
 * @author CHONG
 * @version 1.0 2020年04月24日
 * @see ArithmeticUtils
 * @since 1.0
 */
public final class ArithmeticUtils {

    private static final int DEFAULT_SCALE = 0;

    private static final int ALL_PRECISION = 2;

    /**
     * 提供精确的加法运算，如果某个参数为空，则不作为加算数据
     *
     * @param datas 数据（数值）
     * @return 加算结果（默认为0）
     */
    public static BigDecimal doAdd(int scale, RoundingMode roundingMode, BigDecimal... datas) {
        BigDecimal result = BigDecimal.ZERO;
        for (BigDecimal data : datas) {
            if (null != data) {
                result = result.add(data);
            }
        }
        return round(result, scale, roundingMode);
    }

    /**
     * 提供精确的减法运算，参数为空的作为0处理
     *
     * @param data1 被减数（数值）
     * @param data2 减数（数值）
     * @return 加算结果（默认为0）
     */
    public static BigDecimal subtract(BigDecimal data1, BigDecimal data2) {
        BigDecimal result = BigDecimal.ZERO;
        if (null != data1) {
            result = result.add(data1);
        }
        if (null != data2) {
            result = result.subtract(data2);
        }
        return result;
    }

    /**
     * 提供精确的减法运算，参数为空的作为0处理
     *
     * @param data1 被减数（数值）
     * @param data2 减数（数值）
     * @return 加算结果（默认为0）
     */
    public static BigDecimal subtract(BigDecimal data1, BigDecimal data2, int scale, RoundingMode roundingMode) {
        return round(subtract(data1, data2), scale, roundingMode);
    }

    /**
     * 数组连乘
     * @param values 运算数组
     * @return 连乘结果
     */
    public static BigDecimal multiply(BigDecimal... values) {
        if (values != null && values.length > 0) {
            BigDecimal result = BigDecimal.ONE;
            for(BigDecimal v : values) {
                if(v != null) {
                    result = result.multiply(v);
                }
            }
            return result;
        }
        return null;
    }

    /**
     * 数组连乘
     * @param values 运算数组
     * @return 连乘结果
     */
    public static BigDecimal multiply(int scale, RoundingMode roundingMode, BigDecimal... values) {
        if (values != null && values.length > 0) {
            return round(multiply(values), scale, roundingMode);
        }
        return null;
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1 被除数
     * @param value2 除数
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal value1, BigDecimal value2) {
        return divide(value1, value2, ALL_PRECISION);
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1 被除数
     * @param value2 除数
     * @param scale 精确范围
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal value1, BigDecimal value2, int scale) {
        return divide(value1, value2, scale, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1 被除数
     * @param value2 除数
     * @param scale 精确范围
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal value1, BigDecimal value2, int scale, RoundingMode roundingMode) {
        if (value1 == null || value2 == null || value2.doubleValue() == 0) {
            return BigDecimal.ZERO;
        }
        // 如果精确范围小于0，抛出异常信息
        if (scale < 0) {
            scale = 2;
        }
        return value1.divide(value2, scale, roundingMode);
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1 被除数
     * @param value2 除数
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal value1, int value2) {
        return divide(value1, new BigDecimal(value2), ALL_PRECISION);
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1 被除数
     * @param value2 除数
     * @param scale 精确范围
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal value1, int value2, int scale) {
        if (value1 == null) {
            return value1;
        }
        // 如果精确范围小于0，默认为12
        if (scale < 0) {
            scale = 12;
        }
        return divide(value1, new BigDecimal(value2), scale, RoundingMode.HALF_UP);
    }

    /**
     * 调整值精度
     *
     * @param value 值
     * @param roundingMode 无设置默认四舍五入类型
     * @return double
     */
    public static BigDecimal round(BigDecimal value, RoundingMode roundingMode) {
        if (value == null) {
            return null;
        }
        if (roundingMode == null) {
            roundingMode = RoundingMode.HALF_UP;
        }
        return value.setScale(ALL_PRECISION, roundingMode);
    }


    /**
     * 提供精确的加法运算，如果某个参数为空，则不作为加算数据
     *
     * @param datas 数据（数值）
     * @return 加算结果（默认为0）
     */
    public static BigDecimal doAdd(BigDecimal... datas) {
        BigDecimal result = BigDecimal.ZERO;
        for (BigDecimal data : datas) {
            if (StringUtils.isNotEmpty(data)) {
                result = result.add(data);
            }
        }
        return result;
    }

    /**
     * 提供精确的减法运算，参数为空的作为0处理
     *
     * @param data1 被减数（数值）
     * @param data2 减数（数值）
     * @return 加算结果（默认为0）
     */
    public static BigDecimal doSub(BigDecimal data1, BigDecimal data2) {
        BigDecimal result = BigDecimal.ZERO;
        if (StringUtils.isNotEmpty(data1)) {
            result = result.add(data1);
        }
        if (StringUtils.isNotEmpty(data2)) {
            result = result.subtract(data2);
        }
        return result;
    }

    /**
     * 提供精确乘法运算的mul方法
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal doMul(BigDecimal value1, BigDecimal value2) {
        if (value1 == null) {
            return value1;
        }
        if (value2 == null) {
            return value2;
        }
        return value1.multiply(value2);
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1 被除数
     * @param value2 除数
     * @param scale 精确范围
     * @return 两个参数的商
     */
    public static BigDecimal doDiv(BigDecimal value1, BigDecimal value2, int scale) {
        if (value1 == null) {
            return value1;
        }
        if (value2 == null || equals(value2, BigDecimal.ZERO)) {
            return null;
        }
        // 如果精确范围小于0，抛出异常信息
        if (scale < 0) {
            scale = 2;
        }
        return value1.divide(value2, scale, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1 被除数
     * @param value2 除数
     * @param scale 精确范围
     * @param mode 尾数处理模式
     * @return 两个参数的商
     */
    public static BigDecimal doDiv(BigDecimal value1, BigDecimal value2, int scale,
                                   RoundingMode mode) {
        if (value1 == null) {
            return value1;
        }
        if (value2 == null || equals(value2, BigDecimal.ZERO)) {
            return null;
        }
        // 如果精确范围小于0，抛出异常信息
        if (scale < 0) {
            scale = 2;
        }
        return value1.divide(value2, scale, mode);
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1 被除数
     * @param value2 除数
     * @param scale 精确范围
     * @return 两个参数的商
     */
    public static BigDecimal doDivide(BigDecimal value1, int value2, int scale) {
        return doDiv(value1, new BigDecimal(value2), scale);
    }

    /**
     * 调整值精度
     *
     * @param value 值
     * @param scale 精度
     * @param roundingMode 无设置默认四舍五入类型
     * @return double
     */
    public static BigDecimal round(BigDecimal value, int scale, RoundingMode roundingMode) {
        if (value == null) {
            return null;
        }
        if (roundingMode == null) {
            roundingMode = RoundingMode.HALF_UP;
        }
        return value.setScale(scale, roundingMode);
    }

    /**
     * 判断数字是否相等(包含null的情况)
     *
     * @param value1 数字1
     * @param value2 数字2
     * @return 是否相等
     */
    public static boolean equals(BigDecimal value1, BigDecimal value2) {
        if (value1 != null && value2 != null) {
            return value1.compareTo(value2) == 0;
        }
        return value1 == value2;
    }

    /**
     * 取绝对值
     * @param value 取值对象
     * @return 取值对象的绝对值
     * @see
     * @since 1.0
     */
    public static BigDecimal abs(BigDecimal value) {
        if (value == null) {
            return null;
        }

        return value.abs();
    }

    // add by zhangliang 2017-07-13 start
    /**
     * 提供精确的减法运算，参数为空的作为0处理
     *
     * @param datas 数据（数值）
     * @return 加算结果（默认为0）
     */
    public static BigDecimal doSub(BigDecimal... datas) {
        BigDecimal result = BigDecimal.ZERO;
        if (StringUtils.isNotEmpty(datas[0])) {
            result = result.add(datas[0]);
        }
        for (int i = 1 ; i < datas.length; i++){
            if (StringUtils.isNotEmpty(datas[i])) {
                result = result.subtract(datas[i]);
            }
        }
        return result;
    }
    // add by zhangliang 2017-07-13 end

    /**
     * 取相反数
     * @param value 取值对象
     * @return 取值对象的相反数
     * @see
     * @since 1.0
     */
    public static BigDecimal negate(BigDecimal value) {
        if (value == null) {
            return null;
        }

        return value.negate();
    }
}